package server

import (
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors" // 跨域
	"github.com/gofiber/fiber/v2/middleware/recover"

	// 捕获错误
	"github.com/gofiber/fiber/v2/middleware/requestid" // 请求ID

	"go.uber.org/zap"

	"fiber/config"
	"fiber/global" // 全局类
	"fiber/routes" // 路由

	"strings"
)

var (
	code int
	msg  string
)

func Run() {

	// 初始化fiber
	app := fiber.New(fiber.Config{
		Prefork: true, // 开启多进程
		// 加入全局异常处理
		ErrorHandler: func(c *fiber.Ctx, err error) error {
			/** 注意：
			此处旨在捕获系统运行时出现的各类异常，非必要请不要在代码中使用以下函数
			panic("I'm an error")	// 使用panic，直接进入全局异常
			global.Log.Panic("debug2测试")	// 使用日志记录panic错误，也会进入全局异常，但会多一行panic记录
			*/

			// 对错误信息进行处理
			traceHandler(c, err)

			// 返回错误信息
			return c.Status(code).JSON(fiber.Map{
				"code": code,
				"msg":  msg,
			})
		},
	})

	// 捕获全局错误 在全局异常里面处理 此处不加无法全局错误无法捕获到404错误
	app.Use(recover.New())

	// 跨域 默认所有。
	app.Use(cors.New())

	// 默认请求ID配置
	app.Use(requestid.New())

	// 请求日志
	app.Use(requestLog())

	// 路由
	routes.InitializeRoute(app)

	// 写点别的 测试啥的？
	app.Get("/", func(c *fiber.Ctx) error {
		// panic("I'm an error")	// 使用panic，直接进入全局异常

		return c.SendString("Hello, World 👋!")
	})

	app.Listen(":" + config.App.Port)
}

/*
*
自定义错误记录
内置写法是输出在控制台
改为记录进日志
捕获到的错误记录与想要的不一致 重写捕获
*/
func traceHandler(c *fiber.Ctx, err error) {
	msg = err.Error()

	// 404错误
	if strings.Index(err.Error(), "Cannot") == 0 {
		code = global.NotFoundStatus
		if !config.App.Debug {
			msg = global.NotFoundMsg
		}
	} else if err.Error() == "Method Not Allowed" {
		// Method Not Allowed 请求类型错误
		code = global.AllowedStatus
		if !config.App.Debug {
			msg = global.AllowedMsg
		}
	} else {
		code = global.ServerStatus
		if !config.App.Debug {
			msg = global.ServerMsg
		}
	}

	// 错误信息记入日志
	global.Log.Error(
		"错误日志：\n",
		zap.String("request_id", c.GetRespHeader("X-Request-Id")), // 使用requestId绑定记录
		zap.Error(err),
	)

}

// 将用户请求写进日志
func requestLog() fiber.Handler {
	// 这里只在项目启动时执行一次，暂未理解
	// global.Log.Debug("test")

	return func(c *fiber.Ctx) error {
		// token
		token := c.Get(config.App.TokenIdent)

		// 处理form-data数据 方便查看
		ctype, body := bodyHandle(c)

		// 将用户的请求信息写入日志
		global.Log.Info(
			"请求体：\n",
			zap.String("request_id", c.GetRespHeader("X-Request-Id")), // 使用requestId绑定记录
			zap.String("ip", c.IP()),                                  // 记录的ip或许有问题，后期测下看是否需要重写
			zap.String("url", c.OriginalURL()),
			zap.String("method", c.Method()),
			zap.String("token", token),
			zap.String("content_type", ctype),
			zap.Any("request", body), // 请求结构体
			// 以下存储的是base64编码，暂不细究
			// zap.Reflect("request", body),		// 请求结构体
			// zap.Any("request", body),		// 请求结构体

		)

		return c.Next()
	}

}

// body处理
func bodyHandle(c *fiber.Ctx) (string, interface{}) {
	c_type := c.Get("Content-Type")
	from_str := "multipart/form-data; boundary="
	// from_data 才处理
	index := strings.Index(c_type, from_str)
	if index == -1 {
		// 其他属性原样输出
		return c_type, string(c.Body())
	}

	// from-data
	data, _ := c.MultipartForm()
	// 文件不记录

	return "multipart/form-data", data.Value
}
